package org.iabako.client.ui.form.business.expense;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HTMLPanel;
import org.iabako.client.config.page.PageConfigEnum;
import org.iabako.client.navigation.NavigationController;
import org.iabako.client.tools.IabakoAsyncCallback;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.form.AbstractForm;
import org.iabako.client.ui.layout.field.*;
import org.iabako.client.ui.layout.template.Popup;
import org.iabako.client.ui.layout.widget.dialogbox.IabakoDialogBox;
import org.iabako.client.ui.layout.widget.installment.InstallmentsDefinitionForm;
import org.iabako.client.ui.layout.widget.validation.ValidationMessage;
import org.iabako.client.ui.page.business.expense.ExpenseMake;
import org.iabako.client.ui.page.business.expense.ExpenseSearch;
import org.iabako.client.user.AuthenticateUserProvider;
import org.iabako.shared.entity.business.AbstractEntity;
import org.iabako.shared.entity.business.Expense;
import org.iabako.shared.entity.business.Installment;
import org.iabako.shared.entity.business.InstallmentsDefinition;
import org.iabako.shared.entity.enumeration.*;
import org.iabako.shared.iservice.business.ExpenseServiceAsync;
import org.iabako.shared.tools.GenericTools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Created by jose on 17/10/14.
 */
public abstract class ExpenseForm extends AbstractForm {

    interface MyUiBinder extends UiBinder<HTMLPanel, ExpenseForm> {
    }

    protected MyUiBinder binderForm = GWT.create(MyUiBinder.class);

    protected final ExpenseServiceAsync expenseServiceAsync = ExpenseServiceAsync.Util.getInstance();

    final private static String anotherCategoryLabel = Messages.getLabel("expense_category_other");

    @UiField
    RadioButtonList expenseType;
    @UiField
    InputTextAmount expensePrice;
    @UiField
    InstallmentsDefinitionForm installmentsDefinitionForm;
    @UiField
    InputTextDate expenseDate;
    @UiField
    SelectList paymentMethod;
    @UiField
    InputSwitch payedQuestion;
    @UiField
    InputText anotherCategory;
    @UiField
    SelectList category;
    @UiField
    InputText number;
    @UiField
    InputTextArea description;
    @UiField
    HTMLPanel paymentPanel;
    @UiField
    HTMLPanel installmentDefinitionUndefinedRecapPanel;
    @UiField
    LabelText expenseUndefinedNote;
    @UiField
    LabelText stopExpenseUndefined;
    @UiField
    HTMLPanel definitionPanel;
    @UiField
    DivElement anchor;
    @UiField
    InputSwitch taxIncluded;
    @UiField
    HTMLPanel taxInput;
    @UiField
    InputTextAmount taxPercent;
    @UiField
    LabelText waitCategoriesLabel;
    @UiField
    HTMLPanel installmentsDefinitionFormPanel;

    private Timer timerCategories;

    protected ExpenseForm(PageConfigEnum pageConfigEnum, String explanationMenuLabel) {

        setForm(binderForm, pageConfigEnum, explanationMenuLabel);

        setDefaultValues();

        setHandlers();

        if (this instanceof ExpenseMake) {
            setCategories();
        }

        timerCategories = new Timer() {
            @Override
            public void run() {
                String label = waitCategoriesLabel.getLabelText();
                if (label.contains(" . . .")) {
                    label = label.replace(" . . .", "");
                } else {
                    label += " .";
                }
                waitCategoriesLabel.setLabel(label);
                timerCategories.schedule(200);
            }
        };
        timerCategories.schedule(200);
    }

    private void setDefaultValues() {
        Popup.resizeToSmallPopup();
        installmentsDefinitionForm.initializeFields();

        if (this instanceof ExpenseMake) {
            expenseServiceAsync.getNextAvailableId(new IabakoAsyncCallback<String>() {
                public void onSuccess(String result) {
                    number.setValue(result);
                    super.onSuccess(result);
                }
            });
        }

        expenseServiceAsync.getLastTax(new IabakoAsyncCallback<Double>() {
            public void onSuccess(Double tax) {
                if (taxPercent.getAmountValue() == null && tax != null) {
                    taxPercent.setValue(GenericTools.formatAmount(tax + ""));
                }
                super.onSuccess(tax);
            }
        });
    }

    private void setCategories() {
        expenseServiceAsync.getAllCustomCategories(new IabakoAsyncCallback<List<String>>() {
            @Override
            public void onSuccess(List<String> result) {
                long index = 0;
                for (String customCategory : result) {
                    category.getValues().put(index++, customCategory);
                }
                category.convertEnumListToStringList();
                category.getValues().put(new Long(category.getValues().size()), anotherCategoryLabel);
                category.setValues(category.getValues());

                if (entity != null) {
                    //it's modification mode
                    for (Long i : category.getValues().keySet()) {
                        if (category.getValues().get(i).equals(((Expense) entity).getCategory())) {
                            category.setValue(i);
                            break;
                        }
                    }
                }

                waitCategoriesLabel.setVisible(false);
                timerCategories.cancel();
                category.setVisible(true);

                super.onSuccess(result);
            }
        });
    }

    private void setHandlers() {
        setExpenseTypeHandler();
        setCategoryHandler();
        setTaxIncludedHandler();
        setPaymentDateHandler();
        setPayedQuestionHandler();
    }

    private void setExpenseTypeHandler() {
        for (LabelEnum radioKey : expenseType.getRadioButtons().keySet()) {
            if (ExpenseType.oneShot.equals(radioKey)) {
                expenseType.getRadioButtons().get(radioKey).addHandler(new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        showExpenseTypeValues(true);
                    }
                }, ClickEvent.getType());
            } else {
                expenseType.getRadioButtons().get(radioKey).addHandler(new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        showExpenseTypeValues(false);
                    }
                }, ClickEvent.getType());
            }
        }
    }

    private void showExpenseTypeValues(boolean oneExpense) {
        paymentPanel.setVisible(oneExpense);
        expensePrice.setVisible(oneExpense);
        expenseDate.setVisible(oneExpense);
        payedQuestion.setVisible(oneExpense && expenseDate.getDateValue() != null && expenseDate.getDateValue().compareTo(GenericTools.getDateWithoutTime()) <= 0);
        installmentsDefinitionFormPanel.setVisible(!oneExpense);
        if (!oneExpense) {
            payedQuestion.setValue(false);
            paymentMethod.setVisible(false);
        }
    }

    private void setCategoryHandler() {
        category.getSelectField().addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent event) {
                anotherCategory.setVisible(anotherCategoryLabel.equals(category.getValues().get(category.getValue())));
            }
        });
    }

    private void setTaxIncludedHandler() {
        taxIncluded.addValueChangeHandler(new ValueChangeHandler() {
            public void onValueChange(ValueChangeEvent valueChangeEvent) {
                taxInput.setVisible(taxIncluded.getValue());
                expensePrice.setInputTitle(Messages.getLabel(taxIncluded.getValue() ? "expense_form_price_with_tax" : "expense_form_price"));
            }
        });
    }

    private void setPaymentDateHandler() {
        expenseDate.getInputFieldDate().addValueChangeHandler(new DateHandler());
    }

    private class DateHandler implements ValueChangeHandler<Date> {
        public void onValueChange(ValueChangeEvent<Date> event) {
            boolean paymentDateInPast = expenseDate.getDateValue() != null
                    && expenseDate.getDateValue().compareTo(GenericTools.getDateWithoutTime()) <= 0;
            payedQuestion.setVisible(paymentDateInPast);
            paymentMethod.setVisible(paymentDateInPast && payedQuestion.getValue());

            if (!paymentDateInPast) {
                payedQuestion.setValue(false);
            }
        }
    }

    private void setPayedQuestionHandler() {
        payedQuestion.addValueChangeHandler(new ValueChangeHandler() {
            public void onValueChange(ValueChangeEvent valueChangeEvent) {
                paymentMethod.setVisible(payedQuestion.getValue());
                anchor.scrollIntoView();
            }
        });
    }

    @Override
    protected List<IField> getMandatoryFields() {
        List<IField> mandatoryFields = new ArrayList<IField>();
        mandatoryFields.addAll(Arrays.asList((IField) number, category, anotherCategory));

        boolean isCreation = entity == null || entity.getId() == null;

        if (isCreation) {
            mandatoryFields.add(expenseType);
        }

        if (!anotherCategoryLabel.equals(category.getValues().get(category.getValue()))) {
            mandatoryFields.remove(anotherCategory);
        }
        if (expenseType.getValue() == ExpenseType.oneShot || !isCreation) {
            mandatoryFields.add(expenseDate);
            mandatoryFields.add(expensePrice);
            if (expenseDate.getDateValue() != null && expenseDate.getDateValue().compareTo(GenericTools.getDateWithoutTime()) <= 0) {
                mandatoryFields.add(payedQuestion);
            }
            if (taxIncluded.getValue()) {
                mandatoryFields.add(taxPercent);
            }
        }
        if (expenseType.getValue() == ExpenseType.several && isCreation) {
            mandatoryFields.addAll(installmentsDefinitionForm.getMandatoryFields());
        }
        if (payedQuestion.getValue()) {
            mandatoryFields.add(paymentMethod);
        }
        return mandatoryFields;
    }

    @Override
    protected AbstractEntity getEntity() {
        if (entity == null) {
            entity = new Expense();
        }
        Expense expense = (Expense) entity;

        expense.setNumber(number.getValue());
        if (anotherCategoryLabel.equals(category.getValues().get(category.getValue()))) {
            expense.setCategory(anotherCategory.getValue());
        } else {
            expense.setCategory(category.getValues().get(category.getValue()));
        }
        expense.setDescription(description.getValue());

        if (expense.getId() == null) {
            expense.setExpenseType((ExpenseType) expenseType.getValue());
        }
        if (expense.getId() == null && expense.getExpenseType() == ExpenseType.several) {
            expense.setInstallmentsDefinition(installmentsDefinitionForm.getEntity());
        } else {
            expense.setPaymentDateString(GenericTools.formatDateToString(expenseDate.getDateValue()));
            expense.setPaymentValue(expensePrice.getAmountValue());
            expense.setPaymentMethod(payedQuestion.getValue() ? (PaymentMethod) paymentMethod.getEnumValue() : null);
        }

        if (taxIncluded.getValue()) {
            expense.setTaxPercent(taxPercent.getAmountValue());
        } else {
            expense.setTaxPercent(null);
        }

        return expense;
    }

    public void setExpense(final Expense expense) {

        entity = expense;
        definitionPanel.setVisible(false);

        InstallmentsDefinition installmentsDefinition = expense.getInstallmentsDefinition();
        if (installmentsDefinition != null
                && installmentsDefinition.getInstallmentsType() == InstallmentsType.undefined) {

            installmentDefinitionUndefinedRecapPanel.setVisible(true);
            calculateUndefinedInstallmentsNote();

            final IabakoDialogBox iabakoDialogBox = new IabakoDialogBox(
                    "stop_undefined_expenses_creation_title",
                    "stop_undefined_expenses_creation_question", true,
                    "stop_undefined_expenses_creation_confirm") {
                @Override
                protected void customConfirmButtonHandler() {
                    expenseServiceAsync.stopUndefinedExpensesCreation(expense, new IabakoAsyncCallback<Expense>() {

                        public void onSuccess(Expense result) {
                            entity = result;
                            calculateUndefinedInstallmentsNote();
                            super.onSuccess(result);
                        }
                    });
                }
            };

            iabakoDialogBox.setCloseOnlySimplePopup(true);

            stopExpenseUndefined.getLabel().addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    NavigationController.getInstance().show(iabakoDialogBox);
                }
            });
        }

        //Editable fields
        number.setValue(expense.getNumber());
        setCategories();

        description.setValue(expense.getDescription());
        expenseDate.setValue(expense.getPaymentDate());
        expensePrice.setValue(expense.getPaymentValue() + "");
        expensePrice.setVisible(true);
        expenseDate.setValue(expense.getPaymentDate());
        expenseDate.setVisible(true);

        boolean paymentDateInPast = expenseDate.getDateValue() != null
                && expenseDate.getDateValue().compareTo(GenericTools.getDateWithoutTime()) <= 0;

        if (paymentDateInPast && expense.getPaymentMethod() != null) {
            payedQuestion.setValue(true);
            paymentMethod.setEnumValue(expense.getPaymentMethod());
        } else {
            payedQuestion.setValue(false);
        }
        paymentPanel.setVisible(true);
        payedQuestion.setVisible(paymentDateInPast);
        paymentMethod.setVisible(paymentDateInPast && expense.getPaymentMethod() != null);

        if (expense.getTaxPercent() != null) {
            taxIncluded.setValue(true);
            taxPercent.setValue(GenericTools.formatAmount(expense.getTaxPercent() + ""));
        } else {
            taxIncluded.setValue(false);
        }

        enableAddLinkAction(expenseServiceAsync);
    }

    private void calculateUndefinedInstallmentsNote() {

        InstallmentsDefinition iDef = ((Expense) entity).getInstallmentsDefinition();

        if (!GenericTools.isEmpty(iDef.getStopUndefinedInstallmentsString(), iDef.getLastUndefinedInstallmentString())) {

            stopExpenseUndefined.setVisible(false);
            expenseUndefinedNote.setLabel(Messages.getLabel("expense_form_stopped_undefined_installments_note",
                    GenericTools.formatDate(GenericTools.getDateFromString(iDef.getStopUndefinedInstallmentsString())),
                    GenericTools.formatDate(GenericTools.getDateFromString(iDef.getLastUndefinedInstallmentString()))));

        } else {
            stopExpenseUndefined.setVisible(true);
            expenseUndefinedNote.setLabel(Messages.getLabel("expense_form_undefined_note",
                    iDef.getInstallmentsPeriodicalFrequencyN() + "",
                    iDef.getInstallmentsPeriodicalFrequency().getLabel(),
                    GenericTools.formatDate(iDef.getInstallmentsPeriodicalStartDate()),
                    GenericTools.formatAmount(iDef.getInstallmentsPeriodicalPrice() + ""),
                    GenericTools.formatDate(iDef.getNextUndefinedInstallment())));
        }
    }

    @Override
    public boolean validate() {
        if (!uiValidation()) {
            return false;
        }
        if (expenseType.getValue() == ExpenseType.several) {
            if (!installmentsDefinitionForm.handleUiValidation()) {
                return false;
            }
        }

        Expense expense = (Expense) serverValidation();
        if (expense == null) {
            return false;
        }

        final boolean creation = expense.getId() == null;

        List<Installment> customInstallments = expense.getInstallmentsDefinition() != null ?
                expense.getInstallmentsDefinition().getInstallments() : null;

        expenseServiceAsync.save(expense, customInstallments, new IabakoAsyncCallback<Expense>() {
            public void onSuccess(Expense result) {
                popup.handleClickClose();

                ValidationMessage validationMessageInPageContent = ValidationMessage.getFromRootPage();
                if (creation) {
                    validationMessageInPageContent.showSuccessMessage("validation_expense_form_create_success");
                    AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.expenseNew);
                } else {
                    validationMessageInPageContent.showSuccessMessage("validation_expense_form_modify_success");
                    AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.expenseModify, TrackingType.expensePayed);
                }
                if (NavigationController.getInstance().getRootPage().pageContentComposite instanceof ExpenseSearch) {
                    //If rootPage is ExpenseSearch, we have to refresh search
                    ((ExpenseSearch) NavigationController.getInstance().
                            getRootPage().pageContentComposite).expenseSearchForm.searchForm.search(false);
                }

                AuthenticateUserProvider.getNotificationPanel().refresh(NotificationType.futureExpenses, NotificationType.newPastExpenses);

                super.onSuccess(result);
            }
        });
        return true;
    }
}
